﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Nomo.UnityCoreModule
{
    /// <summary>
    /// 为 <see cref="Transform"/> 类的实例提供扩展方法。
    /// </summary>
    public static class TransformExtension
    {
        /// <summary>
        /// 获取变换的子物体。
        /// </summary>
        /// <param name="transform">变换。</param>
        /// <param name="list">用于存放结果的列表。</param>
        /// <exception cref="ArgumentNullException"><paramref name="transform"/> 或 <paramref name="list"/> 为 <see langword="true"/> .</exception>
        public static void GetChildren(this Transform transform, List<Transform> list)
        {
            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }
            if (list == null)
            {
                throw new ArgumentNullException(nameof(list));
            }
            list.Clear();
            var childCount = transform.childCount;
            for (var i = childCount - 1; i >= 0; i--)
            {
                list.Add(transform.GetChild(i));
            }
        }

        /// <summary>
        /// 交换变换和它的兄弟的层级索引。
        /// </summary>
        /// <param name="transform">变换。</param>
        /// <param name="sibling">变换的兄弟。</param>
        /// <returns>这个变换自身。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="transform"/> 或 <paramref name="sibling"/> 为 <see langword="true"/> .</exception>
        /// <exception cref="ArgumentException"><paramref name="transform"/> 和 <paramref name="sibling"/> 是同一个物体，或 <paramref name="transform"/> 和 <paramref name="sibling"/> 不是兄弟。</exception>
        public static Transform SwapSibling(this Transform transform, Transform sibling)
        {
            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            if (sibling == null)
            {
                throw new ArgumentNullException(nameof(sibling));
            }

            if (transform == sibling)
            {
                throw new ArgumentException();
            }

            if (transform.parent != sibling.parent)
            {
                throw new ArgumentException();
            }

            var index        = transform.GetSiblingIndex();
            var siblingIndex = sibling.GetSiblingIndex();
            if (index < siblingIndex)
            {
                sibling.SetSiblingIndex(index);
                transform.SetSiblingIndex(siblingIndex);
            }
            else
            {
                transform.SetSiblingIndex(siblingIndex);
                sibling.SetSiblingIndex(index);
            }
            return transform;
        }

        /// <summary>
        /// 改变变换的层级索引，让它处于它的兄弟之前。
        /// </summary>
        /// <param name="transform">变换。</param>
        /// <param name="sibling">变换的兄弟。</param>
        /// <returns>这个变换自身。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="transform"/> 或 <paramref name="sibling"/> 为 <see langword="true"/> .</exception>
        /// <exception cref="ArgumentException"><paramref name="transform"/> 和 <paramref name="sibling"/> 是同一个物体，或 <paramref name="transform"/> 和 <paramref name="sibling"/> 不是兄弟。</exception>
        public static Transform SetBeforeSibling(this Transform transform, Transform sibling)
        {
            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            if (sibling == null)
            {
                throw new ArgumentNullException(nameof(sibling));
            }

            if (transform == sibling)
            {
                throw new ArgumentException();
            }

            if (transform.parent != sibling.parent)
            {
                throw new ArgumentException();
            }

            var index        = transform.GetSiblingIndex();
            var siblingIndex = sibling.GetSiblingIndex();
            transform.SetSiblingIndex(index < siblingIndex ? siblingIndex - 1 : siblingIndex);
            return transform;
        }

        /// <summary>
        /// 改变变换的层级索引，让它处于它的兄弟之后。
        /// </summary>
        /// <param name="transform">变换。</param>
        /// <param name="sibling">变换的兄弟。</param>
        /// <returns>这个变换自身。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="transform"/> 或 <paramref name="sibling"/> 为 <see langword="true"/> .</exception>
        /// <exception cref="ArgumentException"><paramref name="transform"/> 和 <paramref name="sibling"/> 是同一个物体，或 <paramref name="transform"/> 和 <paramref name="sibling"/> 不是兄弟。</exception>
        public static Transform SetAfterSibling(this Transform transform, Transform sibling)
        {
            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            if (sibling == null)
            {
                throw new ArgumentNullException(nameof(sibling));
            }

            if (transform == sibling)
            {
                throw new ArgumentException();
            }

            if (transform.parent != sibling.parent)
            {
                throw new ArgumentException();
            }

            var index        = transform.GetSiblingIndex();
            var siblingIndex = sibling.GetSiblingIndex();
            transform.SetSiblingIndex(index < siblingIndex ? siblingIndex : siblingIndex + 1);
            return transform;
        }

        /// <summary>
        /// 删除这个变换的所有子物体。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <returns>这个变换。</returns>
        public static Transform DestroyChildren(this Transform transform)
        {
            for (var i = transform.childCount - 1; i >= 0; i--)
            {
                UnityEngine.Object.Destroy(transform.GetChild(i).gameObject);
            }
            return transform;
        }

        /// <summary>
        /// 立即删除这个变换的所有子物体。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <returns>这个变换。</returns>
        public static Transform DestroyChildrenImmediately(this Transform transform)
        {
            for (var i = transform.childCount - 1; i >= 0; i--)
            {
                UnityEngine.Object.DestroyImmediate(transform.GetChild(i).gameObject);
            }
            return transform;
        }

        /// <summary>
        /// 设置这个变换的位置。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">位置的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="z">位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetPosition(this Transform transform, float? x, float? y, float? z)
        {
            if (!x.HasValue && !y.HasValue && !z.HasValue)
            {
                return transform;
            }

            var position = transform.position;

            if (x.HasValue)
            {
                position.x = x.Value;
            }

            if (y.HasValue)
            {
                position.y = y.Value;
            }

            if (z.HasValue)
            {
                position.z = z.Value;
            }

            transform.position = position;

            return transform;
        }

        /// <summary>
        /// 设置这个变换的位置。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">位置的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetPosition(this Transform transform, float? x, float? y)
        {
            return transform.SetPosition(x, y, null);
        }

        /// <summary>
        /// 设置这个变换的局部位置。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">局部位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">局部位置的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="z">局部位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetLocalPosition(this Transform transform, float? x, float? y, float? z)
        {
            if (!x.HasValue && !y.HasValue && !z.HasValue)
            {
                return transform;
            }

            var localPosition = transform.localPosition;

            if (x.HasValue)
            {
                localPosition.x = x.Value;
            }

            if (y.HasValue)
            {
                localPosition.y = y.Value;
            }

            if (z.HasValue)
            {
                localPosition.z = z.Value;
            }

            transform.localPosition = localPosition;

            return transform;
        }

        /// <summary>
        /// 设置这个变换的局部位置。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">局部位置的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">局部位置的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetLocalPosition(this Transform transform, float? x, float? y)
        {
            return transform.SetLocalPosition(x, y, null);
        }

        /// <summary>
        /// 设置这个变换的局部缩放。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">局部缩放的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">局部缩放的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="z">局部缩放的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetLocalScale(this Transform transform, float? x, float? y, float? z)
        {
            if (!x.HasValue && !y.HasValue && !z.HasValue)
            {
                return transform;
            }

            var localScale = transform.localScale;

            if (x.HasValue)
            {
                localScale.x = x.Value;
            }

            if (y.HasValue)
            {
                localScale.y = y.Value;
            }

            if (z.HasValue)
            {
                localScale.z = z.Value;
            }

            transform.localScale = localScale;

            return transform;
        }

        /// <summary>
        /// 设置这个变换的局部缩放。
        /// </summary>
        /// <param name="transform">这个变换。</param>
        /// <param name="x">局部缩放的 x 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <param name="y">局部缩放的 y 分量（传入 <see langword="null"/> 以保持此分量的值）。</param>
        /// <returns>这个变换。</returns>
        public static Transform SetLocalScale(this Transform transform, float? x, float? y)
        {
            return transform.SetLocalScale(x, y, null);
        }
    }
}
